#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifi_connecter.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "iot_i2c.h"
#include "u8g2.h"
#include "cJSON.h"

#define TIMEOUT 5
#define BUFFER_SIZE 2048
#define API_KEY "YOUR_API_KEY" // 填写心知天气 API 私钥

typedef struct {
    char date[11];   // 日期
    char day[20];    // 早间天气
    char night[20];  // 晚间天气
    int8_t high;     // 最高气温
    int8_t low;      // 最低气温
    int8_t humidity; // 湿度
} WEATHER_DATA;

static const char *g_request_header = "GET %s HTTP/1.1\r\n" \
    "User-Agent: OpenHarmony/Hi3861\r\n" \
    "Accept: */*\r\n" \
    "Host: %s\r\n" \
    "Connection: Close\r\n" \
    "\r\n";

static int g_got_ip = 0;
static ip_addr_t g_dns_addr;
static u8g2_t u8g2;

uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    static uint8_t buffer[32];
    static uint8_t buf_idx;
    uint8_t *data;

    switch (msg)
    {
        case U8X8_MSG_BYTE_SEND:
            data = (uint8_t*) arg_ptr;      
            while (arg_int > 0)
            {
                buffer[buf_idx++] = *data;
                data++;
                arg_int--;
            }
            break;
        case U8X8_MSG_BYTE_INIT:
            // GPIO0 复用为 I2C1 SDA
            IoTGpioInit(0);
            IoTGpioSetFunc(0, IOT_GPIO_FUNC_GPIO_0_I2C1_SDA);
            IoTGpioSetDir(0, IOT_GPIO_DIR_OUT);
            // GPIO1 复用为 I2C1 SCL
            IoTGpioInit(1);
            IoTGpioSetFunc(1, IOT_GPIO_FUNC_GPIO_1_I2C1_SCL);
            IoTGpioSetDir(1, IOT_GPIO_DIR_OUT);
            // 初始化 I2C1，速率 400kbps
            int ret = IoTI2cInit(1, 400000);
            if (ret != IOT_SUCCESS)
            {
                printf("Failed to init I2C! Error=%d\n", ret);
                return 0;
            }
            break;
        case U8X8_MSG_BYTE_SET_DC:
            break;
        case U8X8_MSG_BYTE_START_TRANSFER:
            buf_idx = 0;
            break;
        case U8X8_MSG_BYTE_END_TRANSFER:
            IoTI2cWrite(1, u8x8_GetI2CAddress(u8x8), buffer, buf_idx);
            break;
        default:
            return 0;
    }
    return 1;
}

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t i;
    switch (msg)
    {
        case U8X8_MSG_GPIO_AND_DELAY_INIT:
            break;
        case U8X8_MSG_DELAY_NANO:
            usleep(1);
            break;
        case U8X8_MSG_DELAY_MILLI:
            usleep(arg_int * 1000);
            break;
        default:
            return 0;
    }
    return 1;
}

void dns_found(const char *name, ip_addr_t *ipaddr, void *callback_arg)
{
    if (ipaddr)
    {
        g_got_ip = 1;
        ip_addr_copy(g_dns_addr, *ipaddr);
    }
}

unsigned int http_get(const char *hostname, int port, const char *url, char *recv_buf, size_t max_length)
{
    // DNS 域名解析
    const char *ip = hostname;
    if (hostname[0] < '0' || hostname[0] > '9')
    {
        g_got_ip = 0;
        ip_addr_t ipaddr;
        int ret = dns_gethostbyname(hostname, &ipaddr, dns_found, NULL);
        if (ret != ERR_OK && ret != ERR_INPROGRESS)
        {
            printf("DNS failed, errno=%d\n", ret);
            return -1;
        }
        while (g_got_ip == 0) sleep(1);
        ip = ip_ntoa(&g_dns_addr);
        printf("DNS get ip: %s\n", ip);
    }
    // 填充 sockaddr_in 结构体
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = PP_HTONS(port);
    addr.sin_addr.s_addr = inet_addr(ip);
    // 创建 Socket
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("Socket allocate failed, errno=%d\n", errno);
        return -1;
    }
    struct timeval receiving_timeout = {0};
    receiving_timeout.tv_sec = TIMEOUT;
    // 设置接收超时时间
    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout, sizeof(receiving_timeout)) < 0)
    {
        printf("Set socket receiving timeout failed\n");
        lwip_close(fd);
        return -1;
    }
    printf("Socket allocated\n");
    // 连接 Socket
    if (connect(fd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
    {
        printf("Socket connect failed, errno=%d\n", errno);
        lwip_close(fd);
        return -1;
    }
    printf("Socket connected\n");
    // 发送 HTTP 请求
    char send_buf[512];
    int len = snprintf(send_buf, 512, g_request_header, url, hostname);
    if (lwip_write(fd, send_buf, len) < 0)
    {
        lwip_close(fd);
        return -1;
    }
    printf("Socket send http header success\n");
    // 读取 HTTP 响应
    memset_s(recv_buf, max_length, 0, max_length);
    int length = 0, r = 0;
    do
    {
        r = lwip_read(fd, recv_buf + length, max_length - length - 1);
        if (r > 0) length += r;
    }
    while (r > 0);
    printf("Received from socket, ret=%d errno=%d\n", r, errno);
    lwip_close(fd);
    return strlen(recv_buf);
}

static void HTTPClientTask(void)
{
    // 初始化屏幕
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay);
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
    u8g2_SetFont(&u8g2, u8g2_font_6x12_mr);
    u8g2_ClearBuffer(&u8g2);
    u8g2_DrawStr(&u8g2, 0, 12, "Connecting...");
    u8g2_SendBuffer(&u8g2);
    // 连接至热点
    WifiDeviceConfig config = {0};
    strcpy_s(config.ssid, sizeof(config.ssid), "SSID"); // 填写热点的 SSID
    strcpy_s(config.preSharedKey, sizeof(config.preSharedKey), "PASSWORD"); // 填写热点密码
    config.securityType = WIFI_SEC_TYPE_PSK;
    printf("Connecting wifi...\n");
    int netId = ConnectToHotspot(&config);
    printf("ConnectToHotspot: %d\n", netId);

    sleep(5);

    char response[BUFFER_SIZE];
    while (1)
    {
        int length = http_get("api.seniverse.com", 80, "/v3/weather/daily.json?key="API_KEY"&location=dalian&language=en&days=1", response, BUFFER_SIZE);
        printf("HTTP get, length=%d\n", length);
        if (length > 0)
        {
            // 输出响应内容
            for (int i = 0; i < length; i++)
                putchar(response[i]);
            // 寻找连续两个 \r\n
            char *body = response + 4;
            while (*body != '\0' && strncmp(body - 4, "\r\n\r\n", 4) != 0)
                body++;
            // 解析 JSON
            WEATHER_DATA data;
            cJSON *root = cJSON_Parse(body);
            cJSON *result = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "results"), 0);
            cJSON *daily = cJSON_GetArrayItem(cJSON_GetObjectItem(result, "daily"), 0);
            strcpy(data.date, cJSON_GetStringValue(cJSON_GetObjectItem(daily, "date")));
            strcpy(data.day, cJSON_GetStringValue(cJSON_GetObjectItem(daily, "text_day")));
            strcpy(data.night, cJSON_GetStringValue(cJSON_GetObjectItem(daily, "text_night")));
            data.high = atoi(cJSON_GetStringValue(cJSON_GetObjectItem(daily, "high")));
            data.low = atoi(cJSON_GetStringValue(cJSON_GetObjectItem(daily, "low")));
            data.humidity = atoi(cJSON_GetStringValue(cJSON_GetObjectItem(daily, "humidity")));
            cJSON_Delete(root);
            // 在屏幕上展示数据
            u8g2_ClearBuffer(&u8g2);
            char str[20];
            u8g2_DrawStr(&u8g2, 0, 12, data.date);
            snprintf(str, sizeof(str), "Day: %s", data.day);
            u8g2_DrawStr(&u8g2, 0, 24, str);
            snprintf(str, sizeof(str), "Night: %s", data.night);
            u8g2_DrawStr(&u8g2, 0, 36, str);
            snprintf(str, sizeof(str), "Temperature: %d~%d", data.low, data.high);
            u8g2_DrawStr(&u8g2, 0, 48, str);
            snprintf(str, sizeof(str), "Humidity: %d%%", data.humidity);
            u8g2_DrawStr(&u8g2, 0, 60, str);
            u8g2_SendBuffer(&u8g2);
        }
        // 为了演示只延时 60 秒
        for (int i = 0; i < 60; i++)
            sleep(1);
    }
}

static void HTTPClientDemo(void)
{
    osThreadAttr_t attr;
    attr.name = "HTTPClientTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 40960;
    attr.priority = osPriorityNormal;
    if (osThreadNew(HTTPClientTask, NULL, &attr) == NULL)
    {
        printf("Failed to create HTTPClientTask!\n");
    }
}

SYS_RUN(HTTPClientDemo);
